package lesson4.example3;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

import com.classloading.utils.JavaCompilerUtil;

class TestAOP extends AdviceAdapter {
	Label startLabel = new Label();
	String name;
	
	public TestAOP( final int api,
		      final MethodVisitor mv,
		      final int access,
		      final String name,
		      final String desc){
		super(Opcodes.ASM4, mv, access, name, desc);
	    this.name = name;
	}
	
	protected void onMethodEnter() {
		super.onMethodEnter();
		mv.visitLabel(startLabel);
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		mv.visitLdcInsn("enter method：" + name);
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
	}
	
	public void visitMaxs(int maxStack,int maxLocals){
		// 生成异常表
		Label endLabel = new Label();
		mv.visitTryCatchBlock(startLabel, endLabel, endLabel, null);
		mv.visitLabel(endLabel);
		
		// 造成异常处理代码块
		catchBlock(ATHROW);
		mv.visitInsn(ATHROW);
		super.visitMaxs(maxStack, maxLocals);
	}
	
	private void catchBlock(int opcode){
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		if(opcode == Opcodes.ATHROW){
			mv.visitLdcInsn("error exit method："+name);
		}else{
			mv.visitLdcInsn("normal exit method："+name);
		}
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V",false);
	}

	protected void onMethodExit(int opcode) {
		super.onMethodExit(opcode);
		if (opcode != Opcodes.ATHROW) {
			catchBlock(opcode);
		} 
	}
}

public class TestASM {

	public static String path = JavaCompilerUtil.path +"/bytecode-examples/bytecode-example-4-01/src/main/java/";

	public static String destpath = JavaCompilerUtil.path +"/bytecode-examples/bytecode-example-4-01/output";

	public static void main(String args[]) throws IOException {
//		JavaCompilerUtil.compile(path + "/lesson4/example3/TestDemo.java", destpath);
//		testASM();
//		JavaCompilerUtil.decompile(destpath + "/lesson4/example3/TestDemo.class");
		JavaCompilerUtil.run(new String[]{
		   "java","-cp",destpath,"lesson4.example3.TestDemo"
		});
	}

	public static void testASM() throws IOException{
		byte[] bytes = FileUtils.readFileToByteArray(new File(destpath + "/lesson4/example3/TestDemo.class"));
		ClassReader classReader = new ClassReader(bytes);
		ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
		
		ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM7, classWriter) {
			public MethodVisitor visitMethod(
				int      access, 
				String   name, 
				String   desc, 
				String   signature,
				String[] exceptions
		    ){
				MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
				
				if (!"foo".equals(name)) {
					return methodVisitor;
				}
				// AdviceAdapter方便开发者在方法开始后和结束前插入代码
				return new TestAOP(Opcodes.ASM7,methodVisitor,access,name,desc);
			}
		};

		classReader.accept(classVisitor, 0);
		byte[] bytesModified = classWriter.toByteArray();
		FileUtils.writeByteArrayToFile(new File(destpath + "/lesson4/example3/TestDemo.class"), bytesModified);
	}
}
